package com.gulimall.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.util.ObjectBuilder;
import com.gulimall.common.to.SkuEsModel;
import com.gulimall.search.constant.EsConstant;
import com.gulimall.search.service.MallSearchService;
import com.gulimall.search.vo.SearchParam;
import com.gulimall.search.vo.SearchResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author kkk
 * @date 2022/7/26
 */
@Service
public class MallSearchServiceImpl implements MallSearchService {
    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Override
    public SearchResult search(SearchParam searchParam) {
        SearchResponse<SkuEsModel> search = null;
        SearchResult result = null;
        try {
            search = elasticsearchClient.search(buildSearchRequest(searchParam), SkuEsModel.class);
            result = buildSearchResult(search);
            result.setPageNum(searchParam.getPageNum());
            result.setTotalPages(result.getTotal() % EsConstant.PRODUCT_PAGESIZE == 0 ? 0 :
                    1 + (int) (result.getTotal() / EsConstant.PRODUCT_PAGESIZE));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 准备检索条件
     * 模糊匹配 过滤(按照属性，分类，品牌，价格区间，库存) 排序 高亮 分页 聚合分析
     *
     * @return
     */
    private Function<SearchRequest.Builder, ObjectBuilder<SearchRequest>> buildSearchRequest(SearchParam searchParam) {
        return new Function<SearchRequest.Builder, ObjectBuilder<SearchRequest>>() {
            @Override
            public ObjectBuilder<SearchRequest> apply(SearchRequest.Builder builder) {
                builder.index(EsConstant.PRODUCT_INDEX_NAME).query(w -> {
                    return w.bool(e -> {
                        //模糊查询商品标题
                        if (StringUtils.hasText(searchParam.getKeyword())) {
                            e.must(r -> r.fuzzy(t -> t.field("skuTitle").value(searchParam.getKeyword())));
                        }

                        //查询分类
                        if (searchParam.getCatalog3Id() != null) {
                            e.filter(r -> r.term(t -> t.field("catalogId").value(searchParam.getCatalog3Id())));
                        }

                        //查询是否有货
                        if (searchParam.getHasStock() != null) {
                            e.filter(r -> r.term(t -> t.field("hasStock").value(searchParam.getHasStock() == 1)));
                        }

                        //查询价格区间
                        String skuPrice = searchParam.getSkuPrice();
                        if (StringUtils.hasText(skuPrice)) {
                            e.filter(r -> r.range(t -> {
                                t.field("skuPrice");
                                int index = skuPrice.indexOf('_');
                                String minPrice = skuPrice.substring(0, index);
                                String maxPrice = skuPrice.substring(index + 1, skuPrice.length());
                                if (StringUtils.hasText(minPrice)) {
                                    t.gte(JsonData.of(minPrice));
                                }
                                if (StringUtils.hasText(maxPrice)) {
                                    t.lte(JsonData.of(maxPrice));
                                }
                                return t;
                            }));
                        }

                        //批量查询商品id
                        if (searchParam.getBrandId() != null && searchParam.getBrandId().size() > 0) {
                            List<FieldValue> valueList = new ArrayList<>();
                            for (Long a : searchParam.getBrandId()) {
                                valueList.add(new FieldValue.Builder().longValue(a).build());
                            }
                            e.filter(r -> r.terms(t -> t.field("brandId").terms(y -> y.value(valueList))));
                        }

                        //查询不同属性，以及他们的属性值
                        if (searchParam.getAttrs() != null && searchParam.getAttrs().size() > 0) {
                            e.filter(r -> r.nested(t -> t.path("attrs").query(y -> y.bool(u -> u.must(i -> {
                                for (String attr : searchParam.getAttrs()) {
                                    String[] attrList = attr.split("_");
                                    i.term(o -> o.field("attrs.attrId").value(attrList[0]));
                                    List<FieldValue> fieldValues = new ArrayList<>();
                                    for (String attrValue : attrList[1].split(":")) {
                                        fieldValues.add(new FieldValue.Builder().stringValue(attrValue).build());
                                    }
                                    i.terms(o -> o.field("attrs.attrValue").terms(p -> p.value(fieldValues)));
                                }
                                return i;
                            })))));
                        }
                        return e;
                    });
                }).highlight(w -> {
                    w.fields("skuTitle", e -> e).preTags("<b style='color:red'>").postTags("</b>");
                    return w;
                });

                //分页
                if (searchParam.getPageNum() != null) {
                    builder.from(searchParam.getPageNum()).size(Math.toIntExact(EsConstant.PRODUCT_PAGESIZE));
                }

                //根据条件排序
                if (StringUtils.hasText(searchParam.getSort())) {
                    String[] split = searchParam.getSort().split("_");
                    builder.sort(w -> w.field(e -> {
                        e.field(split[0]).order("asc".equals(split[1]) ? SortOrder.Asc : SortOrder.Desc);
                        return e;
                    }));
                }

                //聚合结果
                Map<String, Aggregation> aggregationMap = new HashMap<>();

                //聚合所有品牌
                Map<String, Aggregation> brandsAggsMap = new HashMap<>();
                //聚合品牌名和品牌图片
                brandsAggsMap.put("brand_name_aggs", new Aggregation.Builder().terms(q -> {
                    return q.field("brandName").size(10);
                }).build());
                brandsAggsMap.put("brand_img_url_aggs", new Aggregation.Builder().terms(q -> {
                    return q.field("brandImg").size(1);
                }).build());

                //聚合品牌id和 其子聚合
                aggregationMap.put("brand_aggs", new Aggregation.Builder().terms(q -> {
                    return q.field("brandId").size(50);
                }).aggregations(brandsAggsMap).build());

                //聚合分类id
                aggregationMap.put("catalog_aggs", new Aggregation.Builder().terms(t -> {
                    return t.field("catalogId").size(1);
                }).aggregations("catalog_name", t -> t.terms(y -> y.field("catalogName").size(1))).build());

                //聚合所有属性
                //聚合属性名字和属性
                Map<String, Aggregation> attrsAggMap = new HashMap<>();
                attrsAggMap.put("attr_name_aggs", new Aggregation.Builder().terms(q -> {
                    return q.field("attrs.attrName").size(1);
                }).build());
                attrsAggMap.put("attr_value_aggs", new Aggregation.Builder().terms(q -> {
                    return q.field("attrs.attrValue").size(50);
                }).build());

                //聚合属性id 及其属性名
                aggregationMap.put("attrs_aggs", new Aggregation.Builder().nested(q -> {
                    return q.path("attrs");
                }).aggregations("attr_id_aggs", new Aggregation.Builder().terms(q -> {
                    return q.field("attrs.attrId").size(10);
                }).aggregations(attrsAggMap).build()).build());

                builder.aggregations(aggregationMap);

                return builder;
            }
        };
    }

    private SearchResult buildSearchResult(SearchResponse<SkuEsModel> searchResponse) {
        SearchResult result = new SearchResult();
        //1.返回所有查询到的商品
        List<SkuEsModel> skuEsModels = searchResponse.hits().hits().stream().map(item -> {
            SkuEsModel source = item.source();
            List<String> skuTitle = item.highlight().get("skuTitle");
            if (skuTitle != null && skuTitle.size() > 0) {
                for (String title : skuTitle) {
                    source.setSkuTitle(title);
                }
            }
            return source;
        }).collect(Collectors.toList());
        result.setProducts(skuEsModels);

        Map<String, Aggregate> aggregations = searchResponse.aggregations();

        //2.当前商品涉及到的所有属性信息
        Map<String, Aggregate> nestedMap = aggregations.get("attrs_aggs").nested().aggregations();
        Aggregate attrIdAggs = nestedMap.get("attr_id_aggs");
        LongTermsAggregate attrIdTerms = attrIdAggs.lterms();
        List<SearchResult.AttrVo> attrVoList = attrIdTerms.buckets().array().stream().map(item -> {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            attrVo.setAttrId(item.key());
            Map<String, Aggregate> attrAggregateMap = item.aggregations();
            //聚合属性名
            for (StringTermsBucket attrNameAggs : attrAggregateMap.get("attr_name_aggs").sterms().buckets().array()) {
                attrVo.setAttrName(attrNameAggs.key());
            }

            //聚合属性值
            List<String> attrValueAggs =
                    attrAggregateMap.get("attr_value_aggs").sterms().buckets().array().stream().map(StringTermsBucket::key).collect(Collectors.toList());
            attrVo.setAttrValue(attrValueAggs);

            return attrVo;
        }).collect(Collectors.toList());
        result.setAttrs(attrVoList);

        //3.当前商品涉及到的所有品牌信息
        Aggregate brandAggs = aggregations.get("brand_aggs");
        List<SearchResult.BrandVo> brandVoList = brandAggs.lterms().buckets().array().stream().map(item -> {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            brandVo.setBrandId(item.key());
            //聚合品牌名
            for (StringTermsBucket brandImgUrlAggs :
                    item.aggregations().get("brand_img_url_aggs").sterms().buckets().array()) {
                brandVo.setBrandImg(brandImgUrlAggs.key());
            }
            //聚合品牌图片
            for (StringTermsBucket brandNameAggs :
                    item.aggregations().get("brand_name_aggs").sterms().buckets().array()) {
                brandVo.setBrandName(brandNameAggs.key());
            }

            return brandVo;
        }).collect(Collectors.toList());
        result.setBrands(brandVoList);

        //4.当前所有商品涉及到的所有分类信息
        Aggregate catalogAggs = aggregations.get("catalog_aggs");
        LongTermsAggregate catalogTerms = catalogAggs.lterms();
        List<SearchResult.CatalogVo> catalogName = catalogTerms.buckets().array().stream().map(item -> {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            catalogVo.setCatalogId(item.key());
            for (StringTermsBucket name : item.aggregations().get("catalog_name").sterms().buckets().array()) {
                catalogVo.setCatalogName(name.key());
            }

            return catalogVo;
        }).collect(Collectors.toList());
        result.setCatalogs(catalogName);

        //5.分页数据
        result.setTotal(searchResponse.hits().total().value());

        return result;
    }
}
